home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Language/OS - Multiplatform Resource Library
/
LANGUAGE OS.iso
/
gnu
/
cvs-1_3.lha
/
cvs-1.3
/
src
/
lock.c
< prev
next >
Wrap
C/C++ Source or Header
|
1992-04-10
|
12KB
|
523 lines
/*
* Copyright (c) 1992, Brian Berliner and Jeff Polk
* Copyright (c) 1989-1992, Brian Berliner
*
* You may distribute under the terms of the GNU General Public License as
* specified in the README file that comes with the CVS 1.3 kit.
*
* Set Lock
*
* Lock file support for CVS.
*/
#include "cvs.h"
#ifndef lint
static char rcsid[] = "@(#)lock.c 1.42 92/04/10";
#endif
extern char *ctime ();
#if __STDC__
static int readers_exist (char *repository);
static int set_lock (char *lockdir, int will_wait, char *repository);
static void set_lockers_name (struct stat *statp);
static int set_writelock_proc (Node * p);
static int unlock_proc (Node * p);
static int write_lock (char *repository);
static void unlock (char *repository);
static void lock_wait ();
#else
static int unlock_proc ();
static void unlock ();
static int set_writelock_proc ();
static int write_lock ();
static int readers_exist ();
static int set_lock ();
static void set_lockers_name ();
static void lock_wait ();
#endif /* __STDC__ */
static char lockers_name[20];
static char *repository;
static char readlock[PATH_MAX], writelock[PATH_MAX];
static int cleanup_lckdir;
static List *locklist;
#define L_OK 0 /* success */
#define L_ERROR 1 /* error condition */
#define L_LOCK_OWNED 2 /* lock already owned by us */
#define L_LOCKED 3 /* lock owned by someone else */
/*
* Clean up all outstanding locks
*/
void
Lock_Cleanup ()
{
/* clean up simple locks (if any) */
if (repository != NULL)
{
unlock (repository);
repository = (char *) NULL;
}
/* clean up multiple locks (if any) */
if (locklist != (List *) NULL)
{
(void) walklist (locklist, unlock_proc);
locklist = (List *) NULL;
}
}
/*
* walklist proc for removing a list of locks
*/
static int
unlock_proc (p)
Node *p;
{
unlock (p->key);
return (0);
}
/*
* Remove the lock files (without complaining if they are not there),
*/
static void
unlock (repository)
char *repository;
{
char tmp[PATH_MAX];
struct stat sb;
if (readlock[0] != '\0')
{
(void) sprintf (tmp, "%s/%s", repository, readlock);
(void) unlink (tmp);
}
if (writelock[0] != '\0')
{
(void) sprintf (tmp, "%s/%s", repository, writelock);
(void) unlink (tmp);
}
/*
* Only remove the lock directory if it is ours, note that this does
* lead to the limitation that one user ID should not be committing
* files into the same Repository directory at the same time. Oh well.
*/
(void) sprintf (tmp, "%s/%s", repository, CVSLCK);
if (stat (tmp, &sb) != -1 && sb.st_uid == geteuid () &&
(writelock[0] != '\0' || (readlock[0] != '\0' && cleanup_lckdir)))
{
(void) rmdir (tmp);
}
cleanup_lckdir = 0;
}
/*
* Create a lock file for readers
*/
int
Reader_Lock (xrepository)
char *xrepository;
{
int err = 0;
FILE *fp;
char tmp[PATH_MAX];
if (noexec)
return (0);
/* we only do one directory at a time for read locks! */
if (repository != NULL)
{
error (0, 0, "Reader_Lock called while read locks set - Help!");
return (1);
}
if (readlock[0] == '\0')
(void) sprintf (readlock, "%s.%d", CVSRFL, getpid ());
/* remember what we're locking (for lock_cleanup) */
repository = xrepository;
/* make sure we clean up on error */
(void) SIG_register (SIGHUP, Lock_Cleanup);
(void) SIG_register (SIGINT, Lock_Cleanup);
(void) SIG_register (SIGQUIT, Lock_Cleanup);
(void) SIG_register (SIGPIPE, Lock_Cleanup);
(void) SIG_register (SIGTERM, Lock_Cleanup);
/* make sure we can write the repository */
(void) sprintf (tmp, "%s/%s.%d", xrepository, CVSTFL, getpid ());
if ((fp = fopen (tmp, "w+")) == NULL || fclose (fp) == EOF)
{
error (0, errno, "cannot create read lock in repository `%s'",
xrepository);
readlock[0] = '\0';
(void) unlink (tmp);
return (1);
}
(void) unlink (tmp);
/* get the lock dir for our own */
(void) sprintf (tmp, "%s/%s", xrepository, CVSLCK);
if (set_lock (tmp, 1, xrepository) != L_OK)
{
error (0, 0, "failed to obtain dir lock in repository `%s'",
xrepository);
readlock[0] = '\0';
return (1);
}
/* write a read-lock */
(void) sprintf (tmp, "%s/%s", xrepository, readlock);
if ((fp = fopen (tmp, "w+")) == NULL || fclose (fp) == EOF)
{
error (0, errno, "cannot create read lock in repository `%s'",
xrepository);
readlock[0] = '\0';
err = 1;
}
/* free the lock dir */
(void) sprintf (tmp, "%s/%s", xrepository, CVSLCK);
if (rmdir (tmp) < 0)
error (0, errno, "failed to remove lock dir `%s'", tmp);
return (err);
}
/*
* Lock a list of directories for writing
*/
static char *lock_error_repos;
static int lock_error;
int
Writer_Lock (list)
List *list;
{
if (noexec)
return (0);
/* We only know how to do one list at a time */
if (locklist != (List *) NULL)
{
error (0, 0, "Writer_Lock called while write locks set - Help!");
return (1);
}
for (;;)
{
/* try to lock everything on the list */
lock_error = L_OK; /* init for set_writelock_proc */
lock_error_repos = (char *) NULL; /* init for set_writelock_proc */
locklist = list; /* init for Lock_Cleanup */
(void) strcpy (lockers_name, "unknown");
(void) walklist (list, set_writelock_proc);
switch (lock_error)
{
case L_ERROR: /* Real Error */
Lock_Cleanup (); /* clean up any locks we set */
error (0, 0, "lock failed - giving up");
return (1);
case L_LOCKED: /* Someone already had a lock */
Lock_Cleanup (); /* clean up any locks we set */
lock_wait (lock_error_repos); /* sleep a while and try again */
continue;
case L_OK: /* we got the locks set */
return (0);
default:
error (0, 0, "unknown lock status %d in Writer_Lock",
lock_error);
return (1);
}
}
}
/*
* walklist proc for setting write locks
*/
static int
set_writelock_proc (p)
Node *p;
{
/* if some lock was not OK, just skip this one */
if (lock_error != L_OK)
return (0);
/* apply the write lock */
lock_error_repos = p->key;
lock_error = write_lock (p->key);
return (0);
}
/*
* Create a lock file for writers returns L_OK if lock set ok, L_LOCKED if
* lock held by someone else or L_ERROR if an error occurred
*/
static int
write_lock (repository)
char *repository;
{
int status;
FILE *fp;
char tmp[PATH_MAX];
if (writelock[0] == '\0')
(void) sprintf (writelock, "%s.%d", CVSWFL, getpid ());
/* make sure we clean up on error */
(void) SIG_register (SIGHUP, Lock_Cleanup);
(void) SIG_register (SIGINT, Lock_Cleanup);
(void) SIG_register (SIGQUIT, Lock_Cleanup);
(void) SIG_register (SIGPIPE, Lock_Cleanup);
(void) SIG_register (SIGTERM, Lock_Cleanup);
/* make sure we can write the repository */
(void) sprintf (tmp, "%s/%s.%d", repository, CVSTFL, getpid ());
if ((fp = fopen (tmp, "w+")) == NULL || fclose (fp) == EOF)
{
error (0, errno, "cannot create write lock in repository `%s'",
repository);
(void) unlink (tmp);
return (L_ERROR);
}
(void) unlink (tmp);
/* make sure the lock dir is ours (not necessarily unique to us!) */
(void) sprintf (tmp, "%s/%s", repository, CVSLCK);
status = set_lock (tmp, 0, repository);
if (status == L_OK || status == L_LOCK_OWNED)
{
/* we now own a writer - make sure there are no readers */
if (readers_exist (repository))
{
/* clean up the lock dir if we created it */
if (status == L_OK)
{
if (rmdir (tmp) < 0)
error (0, errno, "failed to remove lock dir `%s'", tmp);
}
/* indicate we failed due to read locks instead of error */
return (L_LOCKED);
}
/* write the write-lock file */
(void) sprintf (tmp, "%s/%s", repository, writelock);
if ((fp = fopen (tmp, "w+")) == NULL || fclose (fp) == EOF)
{
int xerrno = errno;
(void) unlink (tmp);
/* free the lock dir if we created it */
if (status == L_OK)
{
(void) sprintf (tmp, "%s/%s", repository, CVSLCK);
if (rmdir (tmp) < 0)
error (0, errno, "failed to remove lock dir `%s'", tmp);
}
/* return the error */
error (0, xerrno, "cannot create write lock in repository `%s'",
repository);
return (L_ERROR);
}
return (L_OK);
}
else
return (status);
}
/*
* readers_exist() returns 0 if there are no reader lock files remaining in
* the repository; else 1 is returned, to indicate that the caller should
* sleep a while and try again.
*/
static int
readers_exist (repository)
char *repository;
{
char line[MAXLINELEN];
DIR *dirp;
struct direct *dp;
struct stat sb;
CONST char *regex_err;
int ret = 0;
#ifdef CVS_FUDGELOCKS
again:
#endif
if ((dirp = opendir (repository)) == NULL)
error (1, 0, "cannot open directory %s", repository);
(void) sprintf (line, "^%s.*", CVSRFL);
if ((regex_err = re_comp (line)) != NULL)
error (1, 0, "%s", regex_err);
while ((dp = readdir (dirp)) != NULL)
{
(void) sprintf (line, "%s/%s", repository, dp->d_name);
if (re_exec (dp->d_name))
{
#ifdef CVS_FUDGELOCKS
time_t now;
(void) time (&now);
/*
* If the create time of the file is more than CVSLCKAGE seconds
* ago, try to clean-up the lock file, and if successful, re-open
* the directory and try again.
*/
if (stat (line, &sb) != -1)
{
if (now >= (sb.st_ctime + CVSLCKAGE) && unlink (line) != -1)
{
(void) closedir (dirp);
goto again;
}
set_lockers_name (&sb);
}
#else
if (stat (line, &sb) != -1)
set_lockers_name (&sb);
#endif
ret = 1;
break;
}
}
(void) closedir (dirp);
return (ret);
}
/*
* Set the static variable lockers_name appropriately, based on the stat
* structure passed in.
*/
static void
set_lockers_name (statp)
struct stat *statp;
{
struct passwd *pw;
if ((pw = (struct passwd *) getpwuid (statp->st_uid)) !=
(struct passwd *) NULL)
{
(void) strcpy (lockers_name, pw->pw_name);
}
else
(void) sprintf (lockers_name, "uid%d", statp->st_uid);
}
/*
* Persistently tries to make the directory "lckdir",, which serves as a
* lock. If the create time on the directory is greater than CVSLCKAGE
* seconds old, just try to remove the directory.
*/
static int
set_lock (lockdir, will_wait, repository)
char *lockdir;
int will_wait;
char *repository;
{
struct stat sb;
#ifdef CVS_FUDGELOCKS
time_t now;
#endif
/*
* Note that it is up to the callers of set_lock() to arrange for signal
* handlers that do the appropriate things, like remove the lock
* directory before they exit.
*/
cleanup_lckdir = 0;
for (;;)
{
SIG_beginCrSect ();
if (mkdir (lockdir, 0777) == 0)
{
cleanup_lckdir = 1;
SIG_endCrSect ();
return (L_OK);
}
SIG_endCrSect ();
if (errno != EEXIST)
{
error (0, errno,
"failed to create lock directory in repository `%s'",
repository);
return (L_ERROR);
}
/*
* stat the dir - if it is non-existent, re-try the loop since
* someone probably just removed it (thus releasing the lock)
*/
if (stat (lockdir, &sb) < 0)
{
if (errno == ENOENT)
continue;
error (0, errno, "couldn't stat lock directory `%s'", lockdir);
return (L_ERROR);
}
/*
* if we already own the lock, go ahead and return 1 which means it
* existed but we owned it
*/
if (sb.st_uid == geteuid () && !will_wait)
return (L_LOCK_OWNED);
#ifdef CVS_FUDGELOCKS
/*
* If the create time of the directory is more than CVSLCKAGE seconds
* ago, try to clean-up the lock directory, and if successful, just
* quietly retry to make it.
*/
(void) time (&now);
if (now >= (sb.st_ctime + CVSLCKAGE))
{
if (rmdir (lockdir) >= 0)
continue;
}
#endif
/* set the lockers name */
set_lockers_name (&sb);
/* if he wasn't willing to wait, return an error */
if (!will_wait)
return (L_LOCKED);
lock_wait (repository);
}
}
/*
* Print out a message that the lock is still held, then sleep a while.
*/
static void
lock_wait (repos)
char *repos;
{
time_t now;
(void) time (&now);
error (0, 0, "[%8.8s] waiting for %s's lock in %s", ctime (&now) + 11,
lockers_name, repos);
(void) sleep (CVSLCKSLEEP);
}